package risoe.syslab.fhnew.controller;

/*
 * Copyright (c) 2012-2014, Technical University of Denmark (DTU)
 * All rights reserved.
 * 
 * The Flexhouse 2.0 platform ("this software") is licensed under the
 * BSD 3-clause license.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *   - Neither the name of DTU nor the names of its contributors may be used to
 *     endorse or promote products derived from this software without specific
 *     prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE TECHNICAL UNIVERSITY OF DENMARK BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;

import org.w3c.dom.Document;
import org.w3c.dom.NodeList;

import risoe.syslab.fhnew.common.SystemConstants.DeviceType;
import risoe.syslab.fhnew.config.ActuatorConfig;
import risoe.syslab.fhnew.config.BuildingConfig;
import risoe.syslab.fhnew.config.RoomConfig;
import risoe.syslab.fhnew.config.SensorConfig;
import risoe.syslab.fhnew.gui.DocumentPreparedListener;
import risoe.syslab.fhnew.gui.VisualizationEvent;
import risoe.syslab.fhnew.gui.VisualizationEventListener;
import risoe.syslab.fhnew.gui.VisualizationHolder;
import risoe.syslab.fhnew.gui.VisualizationUpdate;
import risoe.syslab.fhnew.hw.Sensor;
import risoe.syslab.model.CompositeMeasurement;

/**
 * The light controller PowerFlexHouse 3. Handles lighting based on button clicks.
 * @author M. Svendsen
 */
public class DefaultLightController extends DefaultController {
	private LightVisualizationHandler mVisualizationHandler;

	public DefaultLightController(ControllerManager manager, BuildingConfig building) {
		super(manager, building);
	}

	@Override
	public void kill() {
		// Nothing to be done
	}

	@Override
	public VisualizationHolder getVisualization() {
		// Start visualization
		VisualizationHolder holder = new VisualizationHolder(DefaultLightController.class.getSimpleName(), new File(LightVisualizationHandler.PATH).toURI());
		mVisualizationHandler = new LightVisualizationHandler(holder);
		return holder;
	}

	@Override
	public void sensorUpdated(Sensor s) {
		// If this sensor is a button, switch lights in room on or off accordingly
		if (s.getType() == DeviceType.Button) {
			RoomConfig room = mBuildingConfig.getRoomForSensor(s.getName());
			CompositeMeasurement tm = s.getValue();
			if (tm.isGoodProcessValue() && room != null) {
				ArrayList<ActuatorConfig> acts = room.getActuators();
				for (ActuatorConfig act : acts){
					if (act.getType().equals(DeviceType.DimmableLight)){
						setActuator(act.getName(), s.getValue());
					}
				}
			}
		}
		else if (s.getType() == DeviceType.Brightness){
			// Update brightness in visualization
			RoomConfig room = mBuildingConfig.getRoomForSensor(s.getName());
			if (room != null){
				// Compute average brightness in this room
				double averageBrightness = 0.0;
				for (SensorConfig sensor : room.getSensors()){
					if (sensor.getType() == DeviceType.Brightness){
						CompositeMeasurement val = mControllerManager.getSensorValueInternal(sensor.getName());
						if (!Double.isNaN(val.value)){
							averageBrightness += val.value;
						}
					}
				}

				mVisualizationHandler.brightnessReceived(room.getRoomName(), averageBrightness);
			}
		}
	}

	/**
	 * Class responsible for handling the visualization associated with the default light controller
	 * @author M. Svendsen
	 */
	private class LightVisualizationHandler implements DocumentPreparedListener, VisualizationEventListener{
		// Constants
		public static final String PATH = "media/DefaultLightVis.svg";
		public static final String BRIGHTNESSES_ID = "text3183-4-8";

		private HashMap<String, String> mRoomMap = new HashMap<String, String>();
		private Document mDocument;
		private boolean mInitialized = false;
		private VisualizationHolder mVisualizationHolder;

		public LightVisualizationHandler(VisualizationHolder holder) {
			mVisualizationHolder = holder;

			mVisualizationHolder.setOnDocumentPreparedListener(this);
			mVisualizationHolder.setOnVisualizationEventListener(this);
		}

		@Override
		public void onVisualizationEvent(VisualizationEvent event) {	
			if (!mInitialized) return;
		}

		@Override
		public void onDocumentReady(Document document) {
			mDocument = document;

			// Create a map that links room names to SVG ids
			VisualizationUpdate update = getUpdate();
			NodeList nodes = mDocument.getElementById(BRIGHTNESSES_ID).getChildNodes();
			int i = 0;
			for (RoomConfig room : mBuildingConfig.getRooms()){				
				String id = nodes.item(i++).getAttributes().getNamedItem("id").getTextContent();

				// Save the reference
				mRoomMap.put(room.getRoomName(), id);

				// Add command
				update.addCommand(id, room.getRoomName()+": NaN");
			}

			mControllerManager.executeVisualizationUpdateInternal(update);
			mInitialized = true;
		}

		/**
		 * Method for updating the displayed brightness of a room
		 * @param roomName a string representing the name whose brightness has changed
		 * @param value a double representing the new brightness of the room
		 */
		public void brightnessReceived(String roomName, double value){
			if (!mInitialized) return;

			// Update Visualization
			VisualizationUpdate update = getUpdate();
			update.addCommand(mRoomMap.get(roomName), roomName+": "+String.format("%.2f", value));

			mControllerManager.executeVisualizationUpdateInternal(update);
		}

		/**
		 * Convenience method to obtain a VisualizationUpdate
		 */
		private VisualizationUpdate getUpdate(){
			return new VisualizationUpdate(mVisualizationHolder.getName());
		}
	}
}


